[crossover~] 24dB/oct crossover filter
Splits an input signal into two output signals at the crossover frequency

Default frequency is 1000 Hertz
Inlet 1: signal
Outlet 1: low signal
Outlet 2: high signal

Float: crossover frequency
Argument: crossover frequency

Download
crossover~.zip - object, help patch, source code

Source Code
//------------------------------------------------------------------------------
//  Audio Crossover Object for Pd
//
//  crossover~.c
//
//  Cuts an input signal into two output signals at the crossover frequency
//
//  Created by Cooper Baker on 10/29/14.
//  Copyright (c) 2014 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------

// main header for pd
#include "m_pd.h"

// utility header for Pd Spectral Toolkit project
#include "utility.h"

// math header for fancy math
#include <math.h>

// stdio for debugging
#include <stdio.h>

// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif

//------------------------------------------------------------------------------
// definitions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// crossover_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* crossover_class;
static t_class* crossover_arg_class;

//------------------------------------------------------------------------------
// crossover - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct crossover
{
    // this object - must always be first variable in struct
    t_object object;
   
    // needed for CLASS_MAINSIGNALIN macro call in crossover_tilde_setup
    t_float inlet_1;
   
    // coefficients for low pass filters
    double lb0, lb1, lb2, la1, la2;

    // coefficients for high pass filters
    double hb0, hb1, hb2, ha1, ha2;
   
    // sample memory for low pass filters
    double l1x1, l1x2, l1y1, l1y2;
    double l2x1, l2x2, l2y1, l2y2;
   
    // sample memory for high pass filters
    double h1x1, h1x2, h1y1, h1y2;
    double h2x1, h2x2, h2y1, h2y2;
   
    // cutoff frequency for both filters
    t_float freq;
   
    // sample rate
    t_float sr;
   
    // filter quality
    t_float q;

} t_crossover;


//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static void   crossover_float       ( t_crossover* object, t_floatarg number );
static void   crossover_calc_coeffs ( t_crossover* object );
static t_int* crossover_perform     ( t_int* io );
static void   crossover_dsp         ( t_crossover* object, t_signal **sig );
static void*  crossover_new         ( t_symbol* selector, t_int items, t_atom* list );
void          crossover_tilde_setup ( void );


//------------------------------------------------------------------------------
// crossover_calc_coeffs - calculates filter coefficients
//
//      Adapted From:
//      Cookbook formulae for audio EQ biquad filter coefficients
//      By Robert Bristow-Johnson
//      http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
//
//------------------------------------------------------------------------------
static void crossover_calc_coeffs( t_crossover* object )
{
    double w0 = C_2_PI * object->freq / object->sr;
    double cos_w0 = cos( w0 );
    double alpha = sin( w0 ) / ( 2.0 * object->q );
    double a0 = 1 + alpha;
   
    // Lowpass
    //-------------------------------------------
    object->lb0 = ( (  1 - cos_w0 ) * 0.5 ) / a0;
    object->lb1 =   (  1 - cos_w0 )         / a0;
    object->lb2 = ( (  1 - cos_w0 ) * 0.5 ) / a0;
    object->la1 =   ( -2 * cos_w0 )         / a0;
    object->la2 =   (  1 - alpha  )         / a0;
   
    // Highpass
    //-------------------------------------------
    object->hb0 = ( (  1 + cos_w0 ) * 0.5 ) / a0;
    object->hb1 =  -(  1 + cos_w0 )         / a0;
    object->hb2 = ( (  1 + cos_w0 ) * 0.5 ) / a0;
    object->ha1 =   ( -2 * cos_w0 )         / a0;
    object->ha2 =   (  1 - alpha  )         / a0;
}


//------------------------------------------------------------------------------
// crossover_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* crossover_perform( t_int* io )
{
    // store variables from dsp input/output array
    t_float*     in     = ( t_float*     )( io[ 1 ] );
    t_float*     out1   = ( t_float*     )( io[ 2 ] );
    t_float*     out2   = ( t_float*     )( io[ 3 ] );
    t_int        frames = ( t_int        )( io[ 4 ] );
    t_crossover* o      = ( t_crossover* )( io[ 5 ] );
   
    // signal vector iterator variable
    t_int n = -1;
   
    // input sample
    double x_in = 0;
   
    // middle samples
    double x_lm;
    double x_hm;
   
    // output samples
    double y_lp;
    double y_hp;
   
   
    // filter the input samples for each band
    while( ++n < frames )
    {
        // store input sample
        x_in = in[ n ];
       
        // Lowpass
        //------------------------------------------------------------------
       
        // stage one
        x_lm    = o->lb0 * x_in + o->lb1 * o->l1x1 + o->lb2 * o->l1x2 - o->la1 * o->l1y1 - o->la2 * o->l1y2;
        o->l1x2 = o->l1x1;
        o->l1x1 = x_in;
        o->l1y2 = o->l1y1;
        o->l1y1 = x_lm;

        // stage two
        y_lp    = o->lb0 * x_lm + o->lb1 * o->l2x1 + o->lb2 * o->l2x2 - o->la1 * o->l2y1 - o->la2 * o->l2y2;
        o->l2x2 = o->l2x1;
        o->l2x1 = x_lm;
        o->l2y2 = o->l2y1;
        o->l2y1 = y_lp;

        // store output
        out1[ n ] = y_lp;
       
        // Highpass
        //------------------------------------------------------------------
       
        // stage one
        x_hm    = o->hb0 * x_in + o->hb1 * o->h1x1 + o->hb2 * o->h1x2 - o->ha1 * o->h1y1 - o->ha2 * o->h1y2;
        o->h1x2 = o->h1x1;
        o->h1x1 = x_in;
        o->h1y2 = o->h1y1;
        o->h1y1 = x_hm;
       
        // stage two
        y_hp    = o->hb0 * x_hm + o->hb1 * o->h2x1 + o->hb2 * o->h2x2 - o->ha1 * o->h2y1 - o->ha2 * o->h2y2;
        o->h2x2 = o->h2x1;
        o->h2x1 = x_hm;
        o->h2y2 = o->h2y1;
        o->h2y1 = y_hp;
       
        // store output
        out2[ n ] = y_hp;
    }
   
    // return the dsp input/output array address plus one more than its size
    // to provide a pointer to the next perform function in pd's call list
    return &( io[ 6 ] );
}


//------------------------------------------------------------------------------
// crossover_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void crossover_dsp( t_crossover* object, t_signal **sig )
{
    // store sample rate
    object->sr = sig[ 0 ]->s_sr;
   
    // calculate coefficients
    crossover_calc_coeffs( object );
   
    // dsp_add arguments
    //--------------------------------------------------------------------------
    // perform routine
    // number of passed parameters
    // inlet sample vector
    // outlet 1 sample vector
    // outlet 2 sample vector
    // sample frames to process (vector size)
    // object pointer
    dsp_add( crossover_perform, 5, sig[ 0 ]->s_vec, sig[ 1 ]->s_vec, sig[ 2 ]->s_vec, sig[ 0 ]->s_n, object );
}


//------------------------------------------------------------------------------
// crossover_float - handles float input
//------------------------------------------------------------------------------
static void crossover_float( t_crossover* object, t_floatarg number )
{
    object->freq = number;
   
    crossover_calc_coeffs( object );
}


//------------------------------------------------------------------------------
// crossover_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* crossover_new( t_symbol* selector, t_int items, t_atom* list )
{
    // create a pointer to this object
    t_crossover* object = ( t_crossover* )pd_new( crossover_class );
   
    // create new signal outlets for this object
    outlet_new( &object->object, gensym( "signal" ) );
    outlet_new( &object->object, gensym( "signal" ) );
   
    // set quality of filters
    object->q = DbToA( -3.0 );

    // set initial sampling rate in case crossover_calc_coeffs is called
    object->sr = 44100;
   
    // parse object arguments
    if( items > 1 )
    {
        pd_error( object, "crossover~: extra arguments ignored" );
    }
   
    if( items )
    {
        if( list[ 0 ].a_type == A_FLOAT )
        {
            object->freq = atom_getfloatarg( 0, ( int )items, list );
        }
        else
        {
            pd_error( object, "crossover~: invalid argument type" );
        }
       
    }
    else
    {
        object->freq = 1000;
    }
   
    return object;
}


//------------------------------------------------------------------------------
// crossover_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void crossover_tilde_setup( void )
{
    // crossover class
    //--------------------------------------------------------------------------
   
    // creates an instance of this object and describes it to pd
    crossover_class = class_new( gensym( "crossover~" ), ( t_newmethod )crossover_new, 0, sizeof( t_crossover ), 0, A_GIMME, 0 );
   
    // declares leftmost inlet as a signal inlet
    CLASS_MAINSIGNALIN( crossover_class, t_crossover, inlet_1 );
   
    // installs crossover_dsp so that it will be called when dsp is turned on
    class_addmethod( crossover_class, ( t_method )crossover_dsp, gensym( "dsp" ), 0 );
   
    // add float handler
    class_addfloat( crossover_class, crossover_float );
/*
    // crossover arg class
    //--------------------------------------------------------------------------
   
    // creates an instance of this object and describes it to pd
    crossover_arg_class = class_new( gensym( "crossover~" ), 0, 0, sizeof( t_crossover ), 0, 0 );
   
    // declares leftmost inlet as a signal inlet
    CLASS_MAINSIGNALIN( crossover_class, t_crossover, inlet_1 );

    // installs crossover_dsp so that it will be called when dsp is turned on
    class_addmethod( crossover_arg_class, ( t_method )crossover_dsp, gensym( "dsp" ), 0 );
*/

    // announce this object in the pd console
    Announce( "crossover~: splits signal at crossover frequency - v1.0" );
}


//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------